package com.gitee.wsl.jvm.ext.store
import java.net.SocketAddress
import java.nio.ByteBuffer
import java.nio.channels.AsynchronousByteChannel
import java.nio.channels.AsynchronousFileChannel
import java.nio.channels.AsynchronousSocketChannel
import java.nio.channels.CompletionHandler
import java.nio.channels.FileLock
import java.util.concurrent.TimeUnit
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

/**
 * Coroutine's [Continuation] as [CompletionHandler] callback.
 *
 * The attachment parameter to [completed][CompletionHandler.completed] and [failed][CompletionHandler.failed] functions are ignored.
 * You can achieve their application (identifying the callback) by using coroutine local variables.
 */
fun <V> Continuation<V>.asCompletionHandler(): CompletionHandler<V, Unit> = object : CompletionHandler<V, Unit> {
    override fun completed(result: V, attachment: Unit) {
        resume(result)
    }

    override fun failed(exc: Throwable, attachment: Unit) {
        resumeWithException(exc)
    }
}

/**
 * Suspending version of [lock][AsynchronousFileChannel.lock] function.
 *
 * The call is not cancellable (suspends until success or failure), because the underlying channel ([AsynchronousFileChannel]) provides no guarantee for cancellation.
 * Note that closing the channel (probably) continues every call with a failure (and that covers most use cases).
 *
 * However, you can mimic cancellation by ignoring the call (hence, ignoring the results).
 */
 suspend fun AsynchronousFileChannel.lockAsync(
    position: Long = 0,
    size: Long = Long.MAX_VALUE,
    shared: Boolean = false
): FileLock = suspendCoroutine {
    lock(position, size, shared, Unit, it.asCompletionHandler())
}

/**
 * Suspending version of [read][AsynchronousFileChannel.read] function.
 *
 * The call is not cancellable (suspends until success or failure), because the underlying channel ([AsynchronousFileChannel]) provides no guarantee for cancellation.
 * Note that closing the channel (probably) continues every call with a failure (and that covers most use cases).
 *
 * However, you can mimic cancellation by ignoring the call (hence, ignoring the results).
 */
 suspend fun AsynchronousFileChannel.readAsync(
    dst: ByteBuffer,
    position: Long
): Int = suspendCoroutine {
    read(dst, position, Unit, it.asCompletionHandler())
}

/**
 * Suspending version of [write][AsynchronousFileChannel.write] function.
 *
 * The call is not cancellable (suspends until success or failure), because the underlying channel ([AsynchronousFileChannel]) provides no guarantee for cancellation.
 * Note that closing the channel (probably) continues every call with a failure (and that covers most use cases).
 *
 * However, you can mimic cancellation by ignoring the call (hence, ignoring the results).
 */
 suspend fun AsynchronousFileChannel.writeAsync(
    src: ByteBuffer,
    position: Long
): Int = suspendCoroutine {
    write(src, position, Unit, it.asCompletionHandler())
}

/**
 * Suspending version of [read][AsynchronousByteChannel.read] function.
 *
 * The call is not cancellable (suspends until success or failure), because the underlying channel ([AsynchronousByteChannel]) provides no guarantee for cancellation.
 * Note that closing the channel (probably) continues every call with a failure (and that covers most use cases).
 *
 * However, you can mimic cancellation by ignoring the call (hence, ignoring the results).
 */
suspend fun AsynchronousByteChannel.readAsync(dst: ByteBuffer): Int = suspendCoroutine {
    read(dst, Unit, it.asCompletionHandler())
}

/**
 * Suspending version of [write][AsynchronousByteChannel.write] function.
 *
 * The call is not cancellable (suspends until success or failure), because the underlying channel ([AsynchronousByteChannel]) provides no guarantee for cancellation.
 * Note that closing the channel (probably) continues every call with a failure (and that covers most use cases).
 *
 * However, you can mimic cancellation by ignoring the call (hence, ignoring the results).
 */
 suspend fun AsynchronousByteChannel.writeAsync(src: ByteBuffer): Int = suspendCoroutine {
    write(src, Unit, it.asCompletionHandler())
}

/**
 * Suspending version of [connect][AsynchronousSocketChannel.connect] function.
 *
 * The call is not cancellable (suspends until success or failure), because the underlying channel ([AsynchronousSocketChannel]) provides no guarantee for cancellation.
 * Note that closing the channel (probably) continues every call with a failure (and that covers most use cases).
 *
 * However, you can mimic cancellation by ignoring the call (hence, ignoring the results).
 */
suspend fun AsynchronousSocketChannel.connectAsync(remote: SocketAddress) {
    suspendCoroutine<Void> {
        connect(remote, Unit, it.asCompletionHandler())
    }
}

/**
 * Suspending version of [read][AsynchronousSocketChannel.read] function.
 *
 * Reads a sequence of bytes from this channel into the given buffers.
 *
 * The offset and length parameters are always set to 0 and [dsts] size. You can achieve their application by [Array.sliceArray] function and a spread operator.
 *
 * The call is not cancellable (suspends until success or failure), because the underlying channel ([AsynchronousSocketChannel]) provides no guarantee for cancellation.
 * Note that closing the channel (probably) continues every call with a failure (and that covers most use cases).
 *
 * However, you can mimic cancellation by ignoring the call (hence, ignoring the results).
 */
 suspend fun AsynchronousSocketChannel.readAsync(vararg dsts: ByteBuffer): Long = suspendCoroutine {
    read(dsts, 0, dsts.size, -1, TimeUnit.MILLISECONDS, Unit, it.asCompletionHandler())
}

/**
 * Suspending version of [write][AsynchronousSocketChannel.write] function.
 *
 * Reads a sequence of bytes from this channel into the given buffers.
 *
 * * The offset and length parameters are always set to 0 and [srcs] size. You can achieve their application by [Array.sliceArray] function and a spread operator.
 *
 * The call is not cancellable (suspends until success or failure), because the underlying channel ([AsynchronousSocketChannel]) provides no guarantee for cancellation.
 * Note that closing the channel (probably) continues every call with a failure (and that covers most use cases).
 *
 * However, you can mimic cancellation by ignoring the call (hence, ignoring the results).
 */
 suspend fun AsynchronousSocketChannel.writeAsync(vararg srcs: ByteBuffer): Long = suspendCoroutine {
    write(srcs, 0, srcs.size, -1, TimeUnit.MILLISECONDS, Unit, it.asCompletionHandler())
}